import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { VideoFile, ProcessTask, ProcessOptions, TaskStatus } from '@/types/video';
import { videoService } from '@/services/video';

interface VideoState {
  files: VideoFile[];
  currentFile: VideoFile | null;
  tasks: ProcessTask[];
  activeTasks: ProcessTask[];
  recentFiles: VideoFile[];
  currentTask: ProcessTask | null;
  isLoading: boolean;
  uploadProgress: number;
  error: string | null;
  progressSubscriptions: Record<string, () => void>;
}

const initialState: VideoState = {
  files: [],
  currentFile: null,
  tasks: [],
  activeTasks: [],
  recentFiles: [],
  currentTask: null,
  isLoading: false,
  uploadProgress: 0,
  error: null,
  progressSubscriptions: {},
};

// Async thunks
export const selectVideoFiles = createAsyncThunk(
  'video/selectFiles',
  async (_, { rejectWithValue }) => {
    try {
      const response = await videoService.selectFiles();
      if (response.success && response.data) {
        return response.data;
      }
      return rejectWithValue(response.error || 'Failed to select files');
    } catch (error) {
      return rejectWithValue(error instanceof Error ? error.message : 'Failed to select files');
    }
  }
);

export const getVideoInfo = createAsyncThunk(
  'video/getVideoInfo',
  async (path: string, { rejectWithValue }) => {
    try {
      const response = await videoService.getVideoInfo(path);
      if (response.success && response.data) {
        return response.data;
      }
      return rejectWithValue(response.error || 'Failed to get video info');
    } catch (error) {
      return rejectWithValue(error instanceof Error ? error.message : 'Failed to get video info');
    }
  }
);

export const processVideo = createAsyncThunk(
  'video/processVideo',
  async (
    {
      inputPath,
      outputPath,
      options,
    }: { inputPath: string; outputPath: string; options: ProcessOptions },
    { rejectWithValue }
  ) => {
    try {
      const response = await videoService.processVideo(inputPath, outputPath, options);
      if (response.success && response.data) {
        return response.data;
      }
      return rejectWithValue(response.error || 'Failed to start video processing');
    } catch (error) {
      return rejectWithValue(
        error instanceof Error ? error.message : 'Failed to start video processing'
      );
    }
  }
);

export const batchProcessVideos = createAsyncThunk(
  'video/batchProcess',
  async (
    {
      inputPaths,
      outputPath,
      options,
    }: { inputPaths: string[]; outputPath: string; options: ProcessOptions },
    { rejectWithValue }
  ) => {
    try {
      const response = await videoService.batchProcess(inputPaths, outputPath, options);
      if (response.success && response.data) {
        return response.data;
      }
      return rejectWithValue(response.error || 'Failed to start batch processing');
    } catch (error) {
      return rejectWithValue(
        error instanceof Error ? error.message : 'Failed to start batch processing'
      );
    }
  }
);

export const cancelTask = createAsyncThunk(
  'video/cancelTask',
  async (taskId: string, { rejectWithValue }) => {
    try {
      await videoService.cancelProcess(taskId);
      return taskId;
    } catch (error) {
      return rejectWithValue(error instanceof Error ? error.message : 'Failed to cancel task');
    }
  }
);

export const getTaskStatus = createAsyncThunk(
  'video/getTaskStatus',
  async (taskId: string, { rejectWithValue }) => {
    try {
      const response = await videoService.getProcessStatus(taskId);
      if (response.success && response.data) {
        return response.data;
      }
      return rejectWithValue(response.error || 'Failed to get task status');
    } catch (error) {
      return rejectWithValue(error instanceof Error ? error.message : 'Failed to get task status');
    }
  }
);

export const generateThumbnail = createAsyncThunk(
  'video/generateThumbnail',
  async (
    {
      videoPath,
      outputPath,
      timestamp,
    }: { videoPath: string; outputPath: string; timestamp: number },
    { rejectWithValue }
  ) => {
    try {
      const response = await videoService.generateThumbnail(videoPath, outputPath, timestamp);
      if (response.success && response.data) {
        return { videoPath, thumbnailPath: response.data };
      }
      return rejectWithValue(response.error || 'Failed to generate thumbnail');
    } catch (error) {
      return rejectWithValue(
        error instanceof Error ? error.message : 'Failed to generate thumbnail'
      );
    }
  }
);

export const cancelProcess = createAsyncThunk(
  'video/cancelProcess',
  async (_, { getState, rejectWithValue }) => {
    try {
      const state = getState() as { video: VideoState };
      const currentTask = state.video.currentTask;
      if (currentTask) {
        await videoService.cancelProcess(currentTask.id);
        return currentTask.id;
      }
      return rejectWithValue('No active task to cancel');
    } catch (error) {
      return rejectWithValue(error instanceof Error ? error.message : 'Failed to cancel process');
    }
  }
);

// Video slice
const videoSlice = createSlice({
  name: 'video',
  initialState,
  reducers: {
    clearError: state => {
      state.error = null;
    },
    setCurrentFile: (state, action: PayloadAction<VideoFile | null>) => {
      state.currentFile = action.payload;
    },
    addFile: (state, action: PayloadAction<VideoFile>) => {
      const existingIndex = state.files.findIndex(f => f.path === action.payload.path);
      if (existingIndex >= 0) {
        state.files[existingIndex] = action.payload;
      } else {
        state.files.push(action.payload);
      }
    },
    removeFile: (state, action: PayloadAction<string>) => {
      state.files = state.files.filter(f => f.id !== action.payload);
      if (state.currentFile?.id === action.payload) {
        state.currentFile = null;
      }
    },
    updateTaskProgress: (
      state,
      action: PayloadAction<{ taskId: string; progress: Partial<ProcessTask> }>
    ) => {
      const { taskId, progress } = action.payload;
      const taskIndex = state.tasks.findIndex(t => t.id === taskId);
      if (taskIndex >= 0) {
        state.tasks[taskIndex] = { ...state.tasks[taskIndex], ...progress };
      }
      const activeTaskIndex = state.activeTasks.findIndex(t => t.id === taskId);
      if (activeTaskIndex >= 0) {
        state.activeTasks[activeTaskIndex] = { ...state.activeTasks[activeTaskIndex], ...progress };
      }
    },
    addTask: (state, action: PayloadAction<ProcessTask>) => {
      state.tasks.push(action.payload);
      if (['pending', 'queued', 'processing'].includes(action.payload.status)) {
        state.activeTasks.push(action.payload);
      }
    },
    removeTask: (state, action: PayloadAction<string>) => {
      state.tasks = state.tasks.filter(t => t.id !== action.payload);
      state.activeTasks = state.activeTasks.filter(t => t.id !== action.payload);
    },
    updateTaskStatus: (
      state,
      action: PayloadAction<{ taskId: string; status: TaskStatus; result?: any; error?: any }>
    ) => {
      const { taskId, status, result, error } = action.payload;
      const updateTask = (task: ProcessTask) => {
        task.status = status;
        if (result) task.result = result;
        if (error) task.error = error;
        if (['completed', 'failed', 'canceled'].includes(status)) {
          task.completedAt = new Date();
        }
      };

      const taskIndex = state.tasks.findIndex(t => t.id === taskId);
      if (taskIndex >= 0) {
        updateTask(state.tasks[taskIndex]);
      }

      const activeTaskIndex = state.activeTasks.findIndex(t => t.id === taskId);
      if (activeTaskIndex >= 0) {
        updateTask(state.activeTasks[activeTaskIndex]);
        if (['completed', 'failed', 'canceled'].includes(status)) {
          state.activeTasks.splice(activeTaskIndex, 1);
        }
      }
    },
    clearTasks: state => {
      state.tasks = [];
      state.activeTasks = [];
    },
    setUploadProgress: (state, action: PayloadAction<number>) => {
      state.uploadProgress = action.payload;
    },
    setCurrentTask: (state, action: PayloadAction<ProcessTask | null>) => {
      state.currentTask = action.payload;
    },
    startProgressSubscription: (
      state,
      action: PayloadAction<{ taskId: string; cleanup: () => void }>
    ) => {
      const { taskId, cleanup } = action.payload;
      // Store cleanup function
      state.progressSubscriptions[taskId] = cleanup;
    },
    stopProgressSubscription: (state, action: PayloadAction<string>) => {
      const taskId = action.payload;
      const cleanup = state.progressSubscriptions[taskId];
      if (cleanup) {
        cleanup();
        delete state.progressSubscriptions[taskId];
      }
    },
    updateProgressData: (state, action: PayloadAction<{ taskId: string; progress: any }>) => {
      const { taskId, progress } = action.payload;

      // Update current task if it matches
      if (state.currentTask?.id === taskId) {
        state.currentTask.progress = {
          ...state.currentTask.progress,
          ...progress,
        };
      }

      // Update in tasks array
      const taskIndex = state.tasks.findIndex(t => t.id === taskId);
      if (taskIndex >= 0) {
        state.tasks[taskIndex].progress = {
          ...state.tasks[taskIndex].progress,
          ...progress,
        };
      }

      // Update in active tasks
      const activeTaskIndex = state.activeTasks.findIndex(t => t.id === taskId);
      if (activeTaskIndex >= 0) {
        state.activeTasks[activeTaskIndex].progress = {
          ...state.activeTasks[activeTaskIndex].progress,
          ...progress,
        };
      }
    },
  },
  extraReducers: builder => {
    // Select files
    builder
      .addCase(selectVideoFiles.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(selectVideoFiles.fulfilled, (state, _action) => {
        state.isLoading = false;
        // Files will be processed separately via getVideoInfo
      })
      .addCase(selectVideoFiles.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      });

    // Get video info
    builder
      .addCase(getVideoInfo.pending, state => {
        state.isLoading = true;
      })
      .addCase(getVideoInfo.fulfilled, (state, action) => {
        state.isLoading = false;
        const video = action.payload;
        const existingIndex = state.files.findIndex(f => f.path === video.path);
        if (existingIndex >= 0) {
          state.files[existingIndex] = video;
        } else {
          state.files.push(video);
        }
        // Add to recent files
        const recentIndex = state.recentFiles.findIndex(f => f.path === video.path);
        if (recentIndex >= 0) {
          state.recentFiles.splice(recentIndex, 1);
        }
        state.recentFiles.unshift(video);
        // Keep only last 10 recent files
        if (state.recentFiles.length > 10) {
          state.recentFiles = state.recentFiles.slice(0, 10);
        }
      })
      .addCase(getVideoInfo.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      });

    // Process video
    builder
      .addCase(processVideo.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(processVideo.fulfilled, (state, action) => {
        state.isLoading = false;
        const task = action.payload;
        state.tasks.push(task);
        state.activeTasks.push(task);
        state.currentTask = task;
      })
      .addCase(processVideo.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      });

    // Batch process
    builder
      .addCase(batchProcessVideos.pending, state => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(batchProcessVideos.fulfilled, (state, action) => {
        state.isLoading = false;
        const tasks = action.payload;
        state.tasks.push(...tasks);
        state.activeTasks.push(...tasks);
      })
      .addCase(batchProcessVideos.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string;
      });

    // Cancel task
    builder.addCase(cancelTask.fulfilled, (state, action) => {
      const taskId = action.payload;
      const updateTask = (task: ProcessTask) => {
        task.status = 'canceled';
        task.completedAt = new Date();
      };

      const taskIndex = state.tasks.findIndex(t => t.id === taskId);
      if (taskIndex >= 0) {
        updateTask(state.tasks[taskIndex]);
      }

      state.activeTasks = state.activeTasks.filter(t => t.id !== taskId);

      // Clear current task if it was canceled
      if (state.currentTask?.id === taskId) {
        state.currentTask = null;
      }
    });

    // Cancel process
    builder.addCase(cancelProcess.fulfilled, (state, action) => {
      const taskId = action.payload;
      if (state.currentTask?.id === taskId) {
        state.currentTask.status = 'canceled';
        state.currentTask = null;
      }
    });

    // Get task status
    builder.addCase(getTaskStatus.fulfilled, (state, action) => {
      const updatedTask = action.payload;
      const taskIndex = state.tasks.findIndex(t => t.id === updatedTask.id);
      if (taskIndex >= 0) {
        state.tasks[taskIndex] = updatedTask;
      }

      const activeTaskIndex = state.activeTasks.findIndex(t => t.id === updatedTask.id);
      if (activeTaskIndex >= 0) {
        state.activeTasks[activeTaskIndex] = updatedTask;
        if (['completed', 'failed', 'canceled'].includes(updatedTask.status)) {
          state.activeTasks.splice(activeTaskIndex, 1);
        }
      }
    });

    // Generate thumbnail
    builder.addCase(generateThumbnail.fulfilled, (state, action) => {
      const { videoPath, thumbnailPath } = action.payload;
      const fileIndex = state.files.findIndex(f => f.path === videoPath);
      if (fileIndex >= 0) {
        state.files[fileIndex].thumbnail = thumbnailPath;
      }
    });
  },
});

export const {
  clearError,
  setCurrentFile,
  addFile,
  removeFile,
  updateTaskProgress,
  addTask,
  removeTask,
  updateTaskStatus,
  clearTasks,
  setUploadProgress,
  setCurrentTask,
  startProgressSubscription,
  stopProgressSubscription,
  updateProgressData,
} = videoSlice.actions;

export default videoSlice.reducer;
